package top.guoziyang.mydb.backend.tm;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import top.guoziyang.mydb.backend.utils.Panic;
import top.guoziyang.mydb.backend.utils.Parser;
import top.guoziyang.mydb.common.Error;

public class TransactionManagerImpl implements TransactionManager {

    // XID文件头长度
    static final int LEN_XID_HEADER_LENGTH = 8;
    // 每个事务的占用长度
    private static final int XID_FIELD_SIZE = 1;

    // 事务的三种状态
    private static final byte FIELD_TRAN_ACTIVE   = 0;
	private static final byte FIELD_TRAN_COMMITTED = 1;
	private static final byte FIELD_TRAN_ABORTED  = 2;

    // 超级事务，永远为commited状态
    public static final long SUPER_XID = 0;

    static final String XID_SUFFIX = ".xid";
    
    private RandomAccessFile file;
    private FileChannel fc;
    private long xidCounter; // XID 文件管理的事务的个数
    private Lock counterLock;

    TransactionManagerImpl(RandomAccessFile raf, FileChannel fc) {
        this.file = raf;
        this.fc = fc;
//        counterLock 是一个 可重入锁 (ReentrantLock)，用于保护 事务计数器。
//        作用：
//        防止多个线程同时修改事务计数器，避免数据竞争。
//        ReentrantLock 允许 同一个线程多次获取锁，不会发生死锁。
        counterLock = new ReentrantLock();
        checkXIDCounter();
    }

    /**
     * 检查XID文件是否合法
     * 读取XID_FILE_HEADER中的xidcounter，根据它计算文件的理论长度，对比实际长度.
     */
    private void checkXIDCounter() {
        long fileLen = 0;
        try {
            // 获取 XID 文件的长度
            fileLen = file.length();
        } catch (IOException e1) {
            Panic.panic(Error.BadXIDFileException);
        }
        //如果长度小于 XID 头的长度，则抛出异常。
        if(fileLen < LEN_XID_HEADER_LENGTH) {
            Panic.panic(Error.BadXIDFileException);//如果文件长度 小于 XID 头部长度，说明文件损坏，直接报错。
        }

        // 创建一个容量大小为8字节的 ByteBuffer 对象,用于存储从通道读取的数据.
        ByteBuffer buf = ByteBuffer.allocate(LEN_XID_HEADER_LENGTH);
        try {
            fc.position(0);
            fc.read(buf);//存入buf
        } catch (IOException e) {
            Panic.panic(e);
        }
        // buf.array() 会返回缓冲区的内部字节数组;
        // Parser.parseLong() 方法用于将一个字节数组解析为 long 类型的数值
        // 此时xidCounter存储的是该xid文件中管理的事务的个数（不包括超级事务0）
        this.xidCounter = Parser.parseLong(buf.array());
        // 最后一个事务id的下一个id值，即下一个新事务的事务id
        long end = getXidPosition(this.xidCounter + 1);
        if(end != fileLen) {
            Panic.panic(Error.BadXIDFileException);
        }
    }

    /***
     * 根据事务xid取得其在xid文件中对应的位置
     */
    private long getXidPosition(long xid) {
        return LEN_XID_HEADER_LENGTH + (xid-1)*XID_FIELD_SIZE;
    }

    /**
     * updateXID()和incrXIDCounter()方法在执行后都需要立刻刷入文件中，防止在崩溃后文件丢失数据.
     * fileChannel 的 force() 方法，会强制同步缓存内容到文件中
     */
    // 更新xid事务的状态为status
    private void updateXID(long xid, byte status) {
        // 计算该xid所在的索引位置
        long offset = getXidPosition(xid);
        byte[] tmp = new byte[XID_FIELD_SIZE];
        tmp[0] = status;
        ByteBuffer buf = ByteBuffer.wrap(tmp);
        try {
            //将文件的指针移到该xid所在的位置
            fc.position(offset);
            // 重写（更改）xid的状态值
            fc.write(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        try {
//            fc.force(false) 的作用是强制将文件内容写入磁盘，确保文件数据的持久化，但不涉及文件的元数据更新;因为更新原文件是操作系统的事
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    /***
     * 将XidCounter加一，并更新XID Header
     */
    private void incrXIDCounter() {
        xidCounter ++;
        ByteBuffer buf = ByteBuffer.wrap(Parser.long2Byte(xidCounter));
        try {
            fc.position(0);
            fc.write(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        try {
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    /***
     * 开始一个事务，并返回XID
     */
    public long begin() {
        // 加锁
        counterLock.lock();
        try {
            long xid = xidCounter + 1; //新事务的事务id
            updateXID(xid, FIELD_TRAN_ACTIVE); // 设置事务的状态为：正在进行，尚未结束
            incrXIDCounter(); // 更新XID Header的值
            return xid;
        } finally {
            // 释放锁
            counterLock.unlock();
        }
    }

    /***
     * 提交XID事务
     */
    public void commit(long xid) {
        // 设置事务的状态为：已提交
        updateXID(xid, FIELD_TRAN_COMMITTED);
    }

    /***
     * 中止XID事务
     */
    public void abort(long xid) {
        // 设置事务的状态为：已中止
        updateXID(xid, FIELD_TRAN_ABORTED);
    }

    /***
     * 检测XID事务是否处于status状态
     */
    private boolean checkXID(long xid, byte status) {
        long offset = getXidPosition(xid);
        ByteBuffer buf = ByteBuffer.wrap(new byte[XID_FIELD_SIZE]);
        try {
            fc.position(offset);
            fc.read(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        return buf.array()[0] == status;
    }

    /***
     * 判断事务是否是活动状态
     */
    public boolean isActive(long xid) {
        if(xid == SUPER_XID) return false;
        return checkXID(xid, FIELD_TRAN_ACTIVE);
    }

    /***
     * 判断事务是否已提交
     */
    public boolean isCommitted(long xid) {
        if(xid == SUPER_XID) return true;
        return checkXID(xid, FIELD_TRAN_COMMITTED);
    }

    /***
     * 判断事务是否是中止状态
     */
    public boolean isAborted(long xid) {
        if(xid == SUPER_XID) return false;
        return checkXID(xid, FIELD_TRAN_ABORTED);
    }

    public void close() {
        try {
            fc.close();
            file.close();
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

}
